package com.drakulo.games.ais.core;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.drakulo.games.ais.core.building.Building;

/**
 * The GameData contains all data of the current game like resources, buildings,
 * etc.
 * 
 * @author Drakulo
 * 
 */
@Deprecated
public final class OldGameEngine {

	// The game resources
	private static Double gaz;
	private static Double energy;
	private static Double food;
	private static Double cristal;
//	private static Double inhabitants;

	// The current resource modifiers
	private static Map<Resource, Double> modifiers = new HashMap<Resource, Double>();

	// The buildings
	private static List<Building> buildings = new ArrayList<Building>();

	/**
	 * Utility method to get a resource amount
	 * 
	 * @param type
	 *            the resource type
	 * @return the resource amount in the warehouse
	 */
	public static int getRessource(Resource type) {
		Double value = Double.valueOf(0);
		switch (type) {
		case CRISTAL:
			value = cristal;
			break;
		case ENERGY:
			value = energy;
			break;
		case FOOD:
			value = food;
			break;
		case GAS:
			value = gaz;
			break;
		}
		return value.intValue();
	}

	/**
	 * Utility method to get a resource amount
	 * 
	 * @param type
	 *            the resource type
	 * @return the resource amount in the warehouse
	 */
	public static int getMaxStorageSpaceOf(Resource type) {
		int value = 0;
		// for (Building b : buildings) {
		// // value += b.getStoreSpaceFor(type);
		// }
		return value;
	}

	/**
	 * Utility method for updating resources
	 * 
	 * @param type
	 *            the resource type
	 * @param delta
	 *            the delta
	 */
	private static void updateResourceAmount(Resource type, Double delta) {
		switch (type) {
		case CRISTAL:
			cristal += delta;
			break;
		case ENERGY:
			energy += delta;
			break;
		case FOOD:
			food += delta;
			break;
		case GAS:
			gaz += delta;
			break;
		}
	}

	/**
	 * Initializes the given resource amount with the given number
	 * 
	 * @param type
	 * @param value
	 */
	private static void setResourceAmount(Resource type, int value) {
		switch (type) {
		case CRISTAL:
			cristal = Double.valueOf(value);
			break;
		case ENERGY:
			energy = Double.valueOf(value);
			break;
		case FOOD:
			food = Double.valueOf(value);
			break;
		case GAS:
			gaz = Double.valueOf(value);
			break;
		}
	}

	/**
	 * Initializes the game data with default values.
	 */
	public static void initializeGame() {
		buildings = new ArrayList<Building>();
		Map<Resource, Integer> data = new HashMap<Resource, Integer>();
		Resource[] resources = Resource.values();
		for (Resource r : resources) {
			data.put(r, Integer.valueOf(0));
		}
		initializeGame(data);
	}

	/**
	 * Initializes the game data with the given resources
	 * 
	 * @param data
	 *            the resource map
	 */
	public static void initializeGame(Map<Resource, Integer> data) {
		Set<Resource> resources = data.keySet();
		for (Resource r : resources) {
			setResourceAmount(r, data.get(r));
		}
	}

	/**
	 * @return the building list
	 */
	public static List<Building> getBuildings() {
		return buildings;
	}

	/**
	 * Add a building
	 * 
	 * @param b
	 *            the building to add
	 */
	public static void addBuilding(Building b) {
		buildings.add(b);
	}

	/**
	 * This method aggregates buildings productions and consumptions by resource
	 * type
	 * 
	 * @return a map aggregated by resource
	 */
	private static Map<Resource, Double> analyseProduction() {
		Map<Resource, Double> analysis = createDefaultRessourceMap();
		for (Building b : buildings) {
			analyseProductionOf(b, analysis);
		}

		return analysis;
	}

	/**
	 * Analyses the production of abuilding
	 * 
	 * @param b
	 *            the building to analyse
	 * @param analysis
	 *            the map to put results in
	 */
	private static void analyseProductionOf(Building b, Map<Resource, Double> analysis) {
		// Map<Resource, Integer> prod = b.getProduction();
		// // Map<Resource, Integer> cons = b.getConsumption();
		//
		// // Analysis of the building production
		// Set<Resource> resources = prod.keySet();
		// for (Resource r : resources) {
		// // Production is n/min so we divide by 60 to have the production
		// // per second (approx). Use of a helper with BigDecimal for
		// // better and mastered accuracy
		// Double resourceProd = NumberHelper.divide(prod.get(r), 60);
		//
		// // Production is added
		// analysis.put(r, analysis.get(r) + resourceProd);
		// }

		// TODO
		// Analysis of the building consumption
		// resources = cons.keySet();
		// for (Resource r : resources) {
		// // Consumption is n/min so we divide by 60 to have the
		// // consumption per second (approx). Use of a helper with
		// // BigDecimal for
		// // better and mastered accuracy
		// Double resourceCons = NumberHelper.divide(cons.get(r), 60);
		//
		// // Consumption is removed
		// analysis.put(r, analysis.get(r) - resourceCons);
		// }
	}

	/**
	 * Creates a Map with an entry for each Ressource associated with a default
	 * value of 0
	 * 
	 * @return the default map
	 */
	private static Map<Resource, Double> createDefaultRessourceMap() {
		Map<Resource, Double> map = new HashMap<Resource, Double>();
		Resource[] resources = Resource.values();
		for (Resource r : resources) {
			map.put(r, Double.valueOf(0));
		}
		return map;
	}

	/**
	 * By calling this method, production and consumption will be calculated and
	 * applied to the global store.
	 */
	public static void handleCycle() {
		// 1. Analysis of production / consumption
		Map<Resource, Double> analysis = analyseProduction();

		// TODO Handle consumption and negative modifiers
		// 2. If analysis has negative values, we must check if there is enough
		// resources in store to absorb the loss
		// if (hasNegativeValues(analysis)) {
		// boolean enoughResources = true;
		// Set<Resource> resources = analysis.keySet();
		// for (Resource r : resources) {
		// Double resourceDelta = analysis.get(r);
		// if(resourceDelta >= 0){
		// continue;
		// }
		// if (getRessource(r) < -resourceDelta) {
		// enoughResources = false;
		// break;
		// }
		// }
		// if (!enoughResources) {
		// // There is not enought resources in store, here we go for
		// // building selection
		// dispatchAvailableResources();
		// } else {
		// // That's OK, there is enough resources in store for all
		// // buildings
		// allBuildingsCanRun();
		// modifiers = analysis;
		// applyAnalysis(analysis);
		// }
		// } else {
		// There's no negative value, no check to do
		allBuildingsCanRun();
		modifiers = analysis;
		applyAnalysis(analysis);
		// }
	}

	/**
	 * Initializes all buildings with a "can run" flag
	 */
	private static void allBuildingsCanRun() {
		// All buildings can run
		for (Building b : buildings) {
			b.setCanRun(true);
		}
	}

	/**
	 * Verify if there is a negative value for one resource.
	 * 
	 * @param map
	 *            the resource map
	 * @return true if for at least one resource, the value is negative
	 */
	// private static boolean hasNegativeValues(Map<Resource, Double> map) {
	// Set<Resource> resources = map.keySet();
	// for (Resource r : resources) {
	// if (map.get(r) < 0) {
	// return true;
	// }
	// }
	// // No negative value was found
	// return false;
	// }

	/**
	 * This method takes an analysis of resource production and consumption and
	 * applies it to the current stock.
	 * 
	 * @param analysis
	 *            the analysis
	 */
	private static void applyAnalysis(Map<Resource, Double> analysis) {
		Set<Resource> resources = analysis.keySet();
		for (Resource resource : resources) {
			int maxSpace = getMaxStorageSpaceOf(resource);
			Double amount = analysis.get(resource);
			if (getRessource(resource) + amount >= maxSpace) {
				// No more space for this resource. the store value is set to
				// the max
				setResourceAmount(resource, maxSpace);
			} else {
				updateResourceAmount(resource, amount);
			}
		}
	}

	/**
	 * This method is called when there is not enought production nor resources
	 * in store for each building to run properly. A selection of buildings will
	 * be done according to player's settings or default selection by default.
	 * Selected buildings will run properly but other will stop. Ressources
	 * consumed and produced are also updated in there
	 */
	// private static void dispatchAvailableResources() {
	// // Here we have a problem : there is not enought resources for all
	// // buildings to run properly... We have to choose which of them will
	// // have resources to run.
	//
	// // For a first version, we'll use a simple algorithm : first in, first
	// // served. That's for sure not optimized but that's a start point!
	// for (Building b : buildings) {
	// // We have to check each building one by one, performing their
	// // prduction and consumption
	//
	// // First we handle consumption.
	// Map<Resource, Integer> consumption = b.getConsumption();
	// boolean canRun = true;
	// Set<Resource> ressources = consumption.keySet();
	// for (Resource r : ressources) {
	// if (consumption.get(r) > getRessource(r)) {
	// // Not enough resource in store for this building to run.
	// canRun = false;
	// break;
	// }
	// }
	//
	// if (canRun) {
	// // The building can run
	// b.setCanRun(true);
	// // There we calculate the production and consumption analysis...
	// Map<Resource, Double> buildingAnalysis = new HashMap<Resource, Double>();
	// analyseProductionOf(b, buildingAnalysis);
	//
	// // ... and there we apply it to the store
	// applyAnalysis(buildingAnalysis);
	// } else {
	// // The building cannot run
	// b.setCanRun(false);
	// }
	//
	// // TODO handling final modifiers
	// }
	// }

	public static Double getModifierOf(Resource r) {
		return modifiers.get(r);
	}
}
